#!/usr/bin/env python
# coding: utf-8

# 结合手势识别与3D网络可视化
import pygame
from pygame.locals import *
from OpenGL.GL import *
from OpenGL.GLU import *
import numpy as np
import random
import math
import sys
import time
import cv2
import mediapipe as mp
import threading

# ================ MediaPipe手势识别部分 ================
# 导入solution
mp_hands = mp.solutions.hands
# 导入模型
hands = mp_hands.Hands(static_image_mode=False,
                       max_num_hands=1,
                       min_detection_confidence=0.7,
                       min_tracking_confidence=0.5)
# 导入绘图函数
mpDraw = mp.solutions.drawing_utils

# 手势状态
class GestureState:
    def __init__(self):
        self.is_hand_open = False  # 手掌张开状态
        self.is_pointing = False   # 食指指向状态
        self.is_fist = False       # 握拳状态
        self.is_ok_gesture = False # OK手势（拇指和食指形成圈）
        self.hand_x = 0            # 手掌位置X（用于旋转）
        self.hand_y = 0            # 手掌位置Y（用于旋转）
        self.hand_z = 0            # 手掌深度（用于缩放）
        self.last_hand_x = 0       # 上一帧手掌X位置
        self.last_hand_y = 0       # 上一帧手掌Y位置
        self.last_hand_z = 0       # 上一帧手掌深度
        self.scale_change = 0      # 缩放变化量
        self.hand_movement = 0     # 手掌移动程度
        self.is_scale_fixed = False  # 是否固定缩放比例
        self.fixed_scale = 1.0     # 固定的缩放比例
        self.current_scale_normalized = 0.5  # 当前缩放的归一化值(0-1之间)
        self.ok_gesture_start_time = 0  # OK手势开始时间
        self.ok_gesture_duration = 0    # OK手势持续时间
        self.fist_start_time = 0    # 握拳开始时间
        self.fist_duration = 0      # 握拳持续时间

# 全局手势状态
gesture_state = GestureState()

# 摄像头图像
camera_image = None
camera_image_lock = threading.RLock()  # 使用可重入锁，更安全

# 安全获取锁的辅助函数
def safe_acquire_lock(lock, timeout=0.05):
    try:
        acquired = lock.acquire(timeout=timeout)
        return acquired
    except Exception as e:
        print(f"获取锁出错: {e}")
        return False

# 安全释放锁的辅助函数
def safe_release_lock(lock):
    try:
        lock.release()
    except Exception as e:
        print(f"释放锁出错: {e}")

# 处理单帧的函数
def process_frame(img):
    global gesture_state, camera_image, rotation_scale
    
    # 记录该帧开始处理的时间
    start_time = time.time()
    
    # 添加处理时间限制，防止处理时间过长
    max_process_time = 0.1  # 最大处理时间100毫秒
    
    try:
        # 获取图像宽高
        h, w = img.shape[0], img.shape[1]
        
        # 水平镜像翻转图像，使图中左右手与真实左右手对应
        img = cv2.flip(img, 1)
        # BGR转RGB
        img_RGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        # 将RGB图像输入模型，获取预测结果
        results = hands.process(img_RGB)
        
        # 检查是否处理时间过长
        if time.time() - start_time > max_process_time:
            # 如果处理时间过长，放弃本次手势识别，仅更新图像
            with camera_image_lock:
                camera_image = img
            return img

        # 默认不绘制
        is_drawing = False
        show_landmarks = False
        is_deleting = False  # 新增删除模式标志

        if results.multi_hand_landmarks: # 如果有检测到手
            for hand_idx in range(len(results.multi_hand_landmarks)):
                # 获取该手的21个关键点坐标
                hand_21 = results.multi_hand_landmarks[hand_idx]

                # 获取所有手指的指尖和中间关节的y坐标
                y_thumb_tip = hand_21.landmark[4].y
                y_index_tip = hand_21.landmark[8].y
                y_middle_tip = hand_21.landmark[12].y
                y_ring_tip = hand_21.landmark[16].y
                y_pinky_tip = hand_21.landmark[20].y
                
                # 获取x坐标用于检测OK手势
                x_thumb_tip = hand_21.landmark[4].x
                x_index_tip = hand_21.landmark[8].x
                
                # 获取手指PIP关节(第二关节)的y坐标
                y_index_pip = hand_21.landmark[6].y
                y_middle_pip = hand_21.landmark[10].y
                y_ring_pip = hand_21.landmark[14].y
                y_pinky_pip = hand_21.landmark[18].y
                
                # 获取手掌中心位置（用于控制旋转）
                palm_x = hand_21.landmark[0].x  # 手腕点
                palm_y = hand_21.landmark[0].y
                
                # 检查手是否张开（所有手指都伸出）
                is_hand_open = (y_thumb_tip < hand_21.landmark[2].y + 0.1 and  # 增加容差
                               y_index_tip < hand_21.landmark[5].y + 0.1 and
                               y_middle_tip < hand_21.landmark[9].y + 0.1 and
                               y_ring_tip < hand_21.landmark[13].y + 0.1 and
                               y_pinky_tip < hand_21.landmark[17].y + 0.1)
                
                # 检查是否只有食指伸出
                is_pointing = (y_index_tip < y_middle_tip - 0.05 and 
                               y_index_tip < y_ring_tip - 0.05 and 
                               y_index_tip < y_pinky_tip - 0.05)
                
                # 检查是否握拳
                is_fist = (y_index_tip > y_index_pip - 0.05 and  # 添加容差
                          y_middle_tip > y_middle_pip - 0.05 and
                          y_ring_tip > y_ring_pip - 0.05 and
                          y_pinky_tip > y_pinky_pip - 0.05)
                
                # 检测OK手势 - 拇指和食指形成圆圈，其他手指伸直
                # 计算拇指和食指指尖之间的距离
                thumb_index_distance = math.sqrt(
                    (x_thumb_tip - x_index_tip)**2 + 
                    (y_thumb_tip - y_index_tip)**2
                )
                is_ok_gesture = (thumb_index_distance < 0.1 and  # 拇指和食指靠近形成圈
                                y_middle_tip < y_middle_pip and  # 其他手指伸直
                                y_ring_tip < y_ring_pip and
                                y_pinky_tip < y_pinky_pip)
                
                # 更新手势状态
                with camera_image_lock:
                    # 计算手掌移动差值
                    gesture_state.last_hand_x = gesture_state.hand_x
                    gesture_state.last_hand_y = gesture_state.hand_y
                    gesture_state.last_hand_z = gesture_state.hand_z
                    
                    gesture_state.hand_x = palm_x
                    gesture_state.hand_y = palm_y
                    
                    # 计算手掌深度 - 使用手掌宽度作为深度估计的参考(拇指和小指根部的距离)
                    # 距离越大代表越近，距离越小代表越远
                    palm_width = math.sqrt(
                        (hand_21.landmark[1].x - hand_21.landmark[17].x)**2 + 
                        (hand_21.landmark[1].y - hand_21.landmark[17].y)**2
                    )
                    gesture_state.hand_z = palm_width * 10  # 缩放到合适范围
                    
                    # 计算手部移动程度
                    gesture_state.hand_movement = math.sqrt(
                        (gesture_state.hand_x - gesture_state.last_hand_x)**2 +
                        (gesture_state.hand_y - gesture_state.last_hand_y)**2 +
                        (gesture_state.hand_z - gesture_state.last_hand_z)**2
                    ) * 1000  # 放大到合适范围
                
                # 处理复杂逻辑时释放锁，避免长时间锁定
                current_time = time.time()
                
                # 处理OK手势计时
                is_ok_detected = False  # 标记是否需要更新锁定状态
                should_fix_scale = False
                
                if is_ok_gesture:
                    if not gesture_state.is_ok_gesture:  # 第一次做OK手势
                        ok_start_time = current_time
                        ok_duration = 0
                    else:  # 持续做OK手势
                        ok_duration = current_time - gesture_state.ok_gesture_start_time
                        # 如果OK手势持续超过1秒，固定当前缩放比例
                        if ok_duration >= 1.0 and not gesture_state.is_scale_fixed:
                            should_fix_scale = True
                            print("OK gesture confirmed, scale size fixed")
                    is_ok_detected = True
                
                # 握拳处理
                should_release_scale = False
                is_fist_detected = False
                fist_duration = 0
                
                if is_fist:
                    is_fist_detected = True
                    if not gesture_state.is_fist:  # 第一次握拳
                        fist_start_time = current_time
                        fist_duration = 0
                    else:  # 持续握拳
                        fist_duration = current_time - gesture_state.fist_start_time
                        # 如果握拳持续超过2秒，解除固定缩放
                        if fist_duration >= 2.0 and gesture_state.is_scale_fixed:
                            should_release_scale = True
                            print("Fist held for more than 2 seconds, scale size released")
                
                # 再次获取锁来更新状态
                with camera_image_lock:
                    # 更新OK手势状态
                    if is_ok_detected:
                        if not gesture_state.is_ok_gesture:
                            gesture_state.ok_gesture_start_time = current_time
                            gesture_state.ok_gesture_duration = 0
                        else:
                            gesture_state.ok_gesture_duration = ok_duration
                    else:
                        gesture_state.ok_gesture_duration = 0
                    
                    # 更新握拳状态
                    if is_fist_detected:
                        if not gesture_state.is_fist:
                            gesture_state.fist_start_time = current_time
                            gesture_state.fist_duration = 0
                        else:
                            gesture_state.fist_duration = fist_duration
                    else:
                        gesture_state.fist_duration = 0
                    
                    # 更新固定缩放状态
                    if should_fix_scale:
                        gesture_state.is_scale_fixed = True
                        gesture_state.fixed_scale = rotation_scale
                    
                    # 更新解除固定状态
                    if should_release_scale:
                        gesture_state.is_scale_fixed = False
                    
                    # 更新手势状态
                    gesture_state.is_hand_open = is_hand_open
                    gesture_state.is_pointing = is_pointing
                    gesture_state.is_fist = is_fist
                    gesture_state.is_ok_gesture = is_ok_gesture
                    
                    # 更新缩放的归一化值（用于显示进度条）
                    # 范围从0.5到2.0映射到0.0到1.0
                    gesture_state.current_scale_normalized = (rotation_scale - 0.5) / 1.5
                    
                    # 当手掌张开时，计算拇指和小指距离变化来控制缩放
                    if is_hand_open:
                        thumb_to_pinky = math.sqrt(
                            (hand_21.landmark[4].x - hand_21.landmark[20].x)**2 + 
                            (hand_21.landmark[4].y - hand_21.landmark[20].y)**2
                        )
                        # 标准化缩放系数
                        gesture_state.scale_change = (thumb_to_pinky - 0.2) * 10
                
                # 可视化关键点及骨架连线
                mpDraw.draw_landmarks(img, hand_21, mp_hands.HAND_CONNECTIONS)
                
                # 删除所有手势状态信息显示，只保留帧数和进度条
                font = cv2.FONT_HERSHEY_SIMPLEX
                y_pos = 30
                
                # 绘制帧率
                fps = 1/(end_time - start_time)
                cv2.putText(img, f'FPS: {int(fps)}', (10, y_pos), font, 0.7, (255, 0, 255), 2)
                y_pos += 40
                
                # 绘制缩放大小进度条
                bar_width = 200
                bar_height = 20
                bar_x = 10
                bar_y = y_pos  # 放在FPS下方
                
                # 绘制进度条背景
                cv2.rectangle(img, (bar_x, bar_y), (bar_x + bar_width, bar_y + bar_height), (100, 100, 100), -1)
                
                # 绘制进度条填充部分
                fill_width = int(gesture_state.current_scale_normalized * bar_width)
                fill_width = max(0, min(bar_width, fill_width))  # 确保在范围内
                
                # 使用纯绿色作为进度条颜色
                color = (0, 255, 0)  # 绿色(BGR)
                    
                cv2.rectangle(img, (bar_x, bar_y), (bar_x + fill_width, bar_y + bar_height), color, -1)
                
                # 如果缩放已固定，在进度条上显示更明显的标记
                if gesture_state.is_scale_fixed:
                    lock_x = bar_x + fill_width - 10
                    lock_y = bar_y + int(bar_height/2)
                    # 增大圆形并添加边框使其更明显
                    cv2.circle(img, (lock_x, lock_y), 8, (0, 0, 0), 2)  # 黑色边框
                    cv2.circle(img, (lock_x, lock_y), 6, (255, 255, 255), -1)  # 白色填充
                
                # 绘制进度条边框
                cv2.rectangle(img, (bar_x, bar_y), (bar_x + bar_width, bar_y + bar_height), (255, 255, 255), 1)
                
                # 更新y_pos用于后续文本
                y_pos = bar_y + bar_height + 10
        else:
            # 如果没有检测到手，重置手势状态但保留锁定状态
            with camera_image_lock:
                # 检查是否已经持有锁超过最大时间限制
                lock_start_time = time.time()
                max_lock_time = 0.05  # 最大锁定时间50毫秒
                
                gesture_state.is_hand_open = False
                gesture_state.is_pointing = False
                gesture_state.is_fist = False
                gesture_state.is_ok_gesture = False
                gesture_state.ok_gesture_duration = 0
                gesture_state.fist_duration = 0  # 重置握拳计时
                
                # 如果操作耗时过长，释放锁并重新获取
                if time.time() - lock_start_time > max_lock_time:
                    print("Lock operation took too long, releasing and re-acquiring lock")
                
                # 保持上一帧的位置，但标记为没有变化
                gesture_state.last_hand_x = gesture_state.hand_x
                gesture_state.last_hand_y = gesture_state.hand_y
                gesture_state.last_hand_z = gesture_state.hand_z
                # 注意：不重置is_scale_fixed和fixed_scale，保留锁定状态
        
        # 记录帧率和显示（当没有检测到手时）
        end_time = time.time()
        fps = 1/(end_time - start_time)
        font = cv2.FONT_HERSHEY_SIMPLEX
        y_pos = 30
        cv2.putText(img, f'FPS: {int(fps)}', (10, y_pos), font, 0.7, (255, 0, 255), 2)
        
        # 如果没有检测到手，也绘制进度条
        if not results.multi_hand_landmarks:
            y_pos += 40
            bar_width = 200
            bar_height = 20
            bar_x = 10
            bar_y = y_pos
            
            # 绘制进度条背景
            cv2.rectangle(img, (bar_x, bar_y), (bar_x + bar_width, bar_y + bar_height), (100, 100, 100), -1)
            
            # 绘制进度条填充部分
            fill_width = int(gesture_state.current_scale_normalized * bar_width)
            fill_width = max(0, min(bar_width, fill_width))
            
            # 使用纯绿色作为进度条颜色
            color = (0, 255, 0)  # 绿色(BGR)
                
            cv2.rectangle(img, (bar_x, bar_y), (bar_x + fill_width, bar_y + bar_height), color, -1)
            
            # 如果缩放已固定，显示标记
            if gesture_state.is_scale_fixed:
                lock_x = bar_x + fill_width - 10
                lock_y = bar_y + int(bar_height/2)
                # 增大圆形并添加边框使其更明显
                cv2.circle(img, (lock_x, lock_y), 8, (0, 0, 0), 2)  # 黑色边框
                cv2.circle(img, (lock_x, lock_y), 6, (255, 255, 255), -1)  # 白色填充
            
            # 绘制进度条边框
            cv2.rectangle(img, (bar_x, bar_y), (bar_x + bar_width, bar_y + bar_height), (255, 255, 255), 1)
            
            # 更新y_pos用于后续文本
            y_pos = bar_y + bar_height + 10
        
        # 更新摄像头图像（使用带超时的锁）
        acquired = safe_acquire_lock(camera_image_lock)
        if acquired:
            try:
                if camera_image is not None:
                    camera_image = img.copy()  # 使用副本避免引用问题
            finally:
                safe_release_lock(camera_image_lock)
        else:
            print("获取摄像头图像锁超时")
        
        return img
    except Exception as e:
        print(f"处理帧时出错: {e}")
        # 出错时也更新摄像头图像，防止界面卡住
        with camera_image_lock:
            camera_image = img
        return img

# ================ 3D网络球体部分 ================
# 初始化Pygame
pygame.init()
WIDTH, HEIGHT = 1280, 720
display = pygame.display.set_mode((WIDTH, HEIGHT), DOUBLEBUF | OPENGL)
pygame.display.set_caption("手势控制3D网络")

# 基本OpenGL设置
glEnable(GL_DEPTH_TEST)
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

# 设置光照
glEnable(GL_LIGHTING)
glEnable(GL_LIGHT0)
glEnable(GL_COLOR_MATERIAL)
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE)

# 设置光照参数
light_position = [0.0, 0.0, 200.0, 1.0]
light_ambient = [0.3, 0.3, 0.3, 1.0]
light_diffuse = [0.8, 0.8, 0.8, 1.0]
light_specular = [1.0, 1.0, 1.0, 1.0]

glLightfv(GL_LIGHT0, GL_POSITION, light_position)
glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient)
glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse)
glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular)

# 设置视角
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(45, (WIDTH/HEIGHT), 0.1, 1000.0)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glTranslatef(0.0, 0.0, -600)

# 使用顶点数组绘制球体
def create_sphere_vbo(radius, slices, stacks):
    vertices = []
    normals = []
    indices = []
    
    # 生成顶点和法线
    for i in range(stacks + 1):
        phi = math.pi * i / stacks
        for j in range(slices + 1):
            theta = 2.0 * math.pi * j / slices
            
            # 球体坐标
            x = radius * math.sin(phi) * math.cos(theta)
            y = radius * math.sin(phi) * math.sin(theta)
            z = radius * math.cos(phi)
            
            # 单位法线向量
            nx = x / radius
            ny = y / radius
            nz = z / radius
            
            vertices.extend([x, y, z])
            normals.extend([nx, ny, nz])
    
    # 生成索引
    for i in range(stacks):
        for j in range(slices):
            # 每个四边形的两个三角形
            first = i * (slices + 1) + j
            second = first + slices + 1
            
            # 第一个三角形
            indices.extend([first, second, first + 1])
            
            # 第二个三角形
            indices.extend([second, second + 1, first + 1])
    
    # 创建顶点缓冲对象
    vbo_vertices = glGenBuffers(1)
    glBindBuffer(GL_ARRAY_BUFFER, vbo_vertices)
    glBufferData(GL_ARRAY_BUFFER, 
                np.array(vertices, dtype=np.float32),
                GL_STATIC_DRAW)
    
    # 创建法线缓冲对象
    vbo_normals = glGenBuffers(1)
    glBindBuffer(GL_ARRAY_BUFFER, vbo_normals)
    glBufferData(GL_ARRAY_BUFFER, 
                np.array(normals, dtype=np.float32),
                GL_STATIC_DRAW)
    
    # 创建索引缓冲对象
    vbo_indices = glGenBuffers(1)
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo_indices)
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, 
                np.array(indices, dtype=np.uint32),
                GL_STATIC_DRAW)
    
    return {
        'vertices': vbo_vertices,
        'normals': vbo_normals,
        'indices': vbo_indices,
        'count': len(indices)
    }

# 球体类
class Ball:
    def __init__(self, index, sphere_data):
        self.index = index
        self.radius = random.uniform(3, 8)
        self.scale_factor = self.radius / 10.0
        
        # 生成明亮颜色
        group = index % 6
        base_h = group / 6.0
        h = base_h + random.uniform(-0.05, 0.05)
        s = random.uniform(0.7, 1)
        v = random.uniform(0.8, 1)
        r, g, b = self._hsv_to_rgb(h, s, v)
        
        self.color = (r, g, b, 0.9)
        
        # 位置生成
        r = 170 * math.pow(random.random(), 0.5)
        theta = random.uniform(0, math.pi * 2)
        phi = random.uniform(0, math.pi)
        
        self.x = r * math.sin(phi) * math.cos(theta)
        self.y = r * math.sin(phi) * math.sin(theta)
        self.z = r * math.cos(phi)
        
        # 随机速度
        self.speed = [random.uniform(-1.2, 1.2) for _ in range(3)]
        
        # 存储球体VBO数据的引用
        self.sphere_data = sphere_data
        
        # 上次更新时间
        self.last_update = time.time()
    
    def _hsv_to_rgb(self, h, s, v):
        if s == 0.0:
            return v, v, v
        
        i = int(h * 6)
        f = (h * 6) - i
        p = v * (1 - s)
        q = v * (1 - s * f)
        t = v * (1 - s * (1 - f))
        i %= 6
        
        if i == 0: return v, t, p
        if i == 1: return q, v, p
        if i == 2: return p, v, t
        if i == 3: return p, q, v
        if i == 4: return t, p, v
        if i == 5: return v, p, q
    
    def update(self, dt):
        # 使用帧时间差来平滑动画
        dt = min(dt, 0.05)
        
        # 更新位置
        self.x += self.speed[0] * dt * 30
        self.y += self.speed[1] * dt * 30
        self.z += self.speed[2] * dt * 30
        
        # 碰撞检测
        container_radius = 190
        distance = math.sqrt(self.x**2 + self.y**2 + self.z**2)
        
        if distance + self.radius > container_radius:
            # 法线向量
            nx = self.x / distance
            ny = self.y / distance
            nz = self.z / distance
            
            # 速度点积
            dot = self.speed[0] * nx + self.speed[1] * ny + self.speed[2] * nz
            
            # 反弹
            loss = 0.98
            self.speed[0] = (self.speed[0] - 2 * dot * nx) * loss
            self.speed[1] = (self.speed[1] - 2 * dot * ny) * loss
            self.speed[2] = (self.speed[2] - 2 * dot * nz) * loss
            
            # 调整位置
            adj_radius = container_radius - self.radius - 1
            self.x = nx * adj_radius
            self.y = ny * adj_radius
            self.z = nz * adj_radius
            
        # 更新时间
        self.last_update = time.time()
    
    def draw(self):
        glPushMatrix()
        glTranslatef(self.x, self.y, self.z)
        glScalef(self.scale_factor, self.scale_factor, self.scale_factor)
        
        # 设置颜色
        glColor4f(*self.color)
        
        # 启用顶点和法线数组
        glEnableClientState(GL_VERTEX_ARRAY)
        glEnableClientState(GL_NORMAL_ARRAY)
        
        # 绑定顶点缓冲对象
        glBindBuffer(GL_ARRAY_BUFFER, self.sphere_data['vertices'])
        glVertexPointer(3, GL_FLOAT, 0, None)
        
        # 绑定法线缓冲对象
        glBindBuffer(GL_ARRAY_BUFFER, self.sphere_data['normals'])
        glNormalPointer(GL_FLOAT, 0, None)
        
        # 绑定索引缓冲对象并绘制
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.sphere_data['indices'])
        glDrawElements(GL_TRIANGLES, self.sphere_data['count'], GL_UNSIGNED_INT, None)
        
        # 禁用顶点和法线数组
        glDisableClientState(GL_VERTEX_ARRAY)
        glDisableClientState(GL_NORMAL_ARRAY)
        
        glPopMatrix()

def calculate_distance(ball1, ball2):
    return math.sqrt(
        (ball1.x - ball2.x)**2 + 
        (ball1.y - ball2.y)**2 + 
        (ball1.z - ball2.z)**2
    )

# 创建球体VBO和小球
sphere_data = create_sphere_vbo(10.0, 16, 16)
num_balls = 120  # 减少球数量以提高性能
balls = [Ball(i, sphere_data) for i in range(num_balls)]

# 旋转状态
rotation_x = 0
rotation_y = 0
rotation_scale = 1.0

# 保存好看的视角
favorite_views = [
    {'x': 15, 'y': 45, 'scale': 1.2},
    {'x': -20, 'y': 120, 'scale': 0.9},
    {'x': 0, 'y': 180, 'scale': 1.0},
]
current_view = 0

# 连接线渲染设置
connection_distance = 60
max_connections = 5000

# 颜色周期效果
color_cycle_speed = 0.2
color_cycle_offset = 0

# 创建摄像头纹理
def create_camera_texture():
    texture_id = glGenTextures(1)
    glBindTexture(GL_TEXTURE_2D, texture_id)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
    return texture_id

camera_texture = create_camera_texture()

# 更新摄像头纹理，添加超时处理
def update_camera_texture(img):
    if img is None:
        return
    
    try:
        # 将OpenCV图像转换为OpenGL纹理
        img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        img_rgb = cv2.resize(img_rgb, (320, 240))  # 调整大小
        
        # 尝试更新纹理，如果失败则跳过
        try:
            glBindTexture(GL_TEXTURE_2D, camera_texture)
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, img_rgb.shape[1], img_rgb.shape[0], 
                        0, GL_RGB, GL_UNSIGNED_BYTE, img_rgb)
        except Exception as e:
            print(f"更新纹理出错: {e}")
    except Exception as e:
        print(f"处理纹理图像出错: {e}")

# 显示摄像头图像
def draw_camera_view():
    try:
        local_image = None
        
        # 尝试获取摄像头图像的副本
        acquired = safe_acquire_lock(camera_image_lock)
        if acquired:
            try:
                if camera_image is not None:
                    local_image = camera_image.copy()
            finally:
                safe_release_lock(camera_image_lock)
        
        if local_image is None:
            # 如果没有图像或获取锁失败，绘制一个灰色框代替
            glDisable(GL_LIGHTING)
            glMatrixMode(GL_PROJECTION)
            glPushMatrix()
            glLoadIdentity()
            glOrtho(0, WIDTH, 0, HEIGHT, -1, 1)
            
            glMatrixMode(GL_MODELVIEW)
            glPushMatrix()
            glLoadIdentity()
            
            # 绘制灰色框
            glColor4f(0.3, 0.3, 0.3, 1.0)
            glBegin(GL_QUADS)
            glVertex2f(WIDTH-320, 0)
            glVertex2f(WIDTH, 0)
            glVertex2f(WIDTH, 240)
            glVertex2f(WIDTH-320, 240)
            glEnd()
            
            glPopMatrix()
            glMatrixMode(GL_PROJECTION)
            glPopMatrix()
            glMatrixMode(GL_MODELVIEW)
            glEnable(GL_LIGHTING)
            return
        
        # 正常渲染摄像头图像
        try:
            # 将图像转换为OpenGL纹理
            img_rgb = cv2.cvtColor(local_image, cv2.COLOR_BGR2RGB)
            img_rgb = cv2.resize(img_rgb, (320, 240))  # 调整大小
            
            glBindTexture(GL_TEXTURE_2D, camera_texture)
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, img_rgb.shape[1], img_rgb.shape[0], 
                        0, GL_RGB, GL_UNSIGNED_BYTE, img_rgb)
            
            # 禁用光照并启用纹理
            glDisable(GL_LIGHTING)
            glEnable(GL_TEXTURE_2D)
            
            # 在屏幕右下角绘制摄像头图像
            glMatrixMode(GL_PROJECTION)
            glPushMatrix()
            glLoadIdentity()
            glOrtho(0, WIDTH, 0, HEIGHT, -1, 1)
            
            glMatrixMode(GL_MODELVIEW)
            glPushMatrix()
            glLoadIdentity()
            
            glColor4f(1, 1, 1, 1)
            glBegin(GL_QUADS)
            glTexCoord2f(0, 1); glVertex2f(WIDTH-320, 0)
            glTexCoord2f(1, 1); glVertex2f(WIDTH, 0)
            glTexCoord2f(1, 0); glVertex2f(WIDTH, 240)
            glTexCoord2f(0, 0); glVertex2f(WIDTH-320, 240)
            glEnd()
            
            glPopMatrix()
            glMatrixMode(GL_PROJECTION)
            glPopMatrix()
            glMatrixMode(GL_MODELVIEW)
            
            glDisable(GL_TEXTURE_2D)
            glEnable(GL_LIGHTING)
        except Exception as e:
            print(f"绘制摄像头视图时出错: {e}")
            # 出错后尝试恢复OpenGL状态
            glDisable(GL_TEXTURE_2D)
            glEnable(GL_LIGHTING)
            glMatrixMode(GL_MODELVIEW)
    except Exception as e:
        print(f"绘制摄像头视图时发生严重错误: {e}")
        # 确保返回正确的状态
        glDisable(GL_TEXTURE_2D)
        glEnable(GL_LIGHTING)
        glMatrixMode(GL_MODELVIEW)

# 启动摄像头线程
def camera_thread_function():
    cap = cv2.VideoCapture(0)
    
    try:
        while running:
            success, frame = cap.read()
            if not success:
                time.sleep(0.1)  # 添加短暂延时避免CPU占用过高
                continue
            
            try:
                # 处理帧 - 减少锁的使用时间
                local_frame = frame.copy()  # 创建副本以减少锁定时间
                process_frame(local_frame)
            except Exception as e:
                print(f"处理帧时出错: {e}")
                # 出错时也更新摄像头图像，防止界面卡住
                with camera_image_lock:
                    camera_image = frame
            
            # 延迟以控制FPS，但确保不会太长
            time.sleep(0.01)
    except Exception as e:
        print(f"摄像头线程错误: {e}")
        import traceback
        traceback.print_exc()
    finally:
        cap.release()
        print("摄像头线程结束")

# 主循环
running = True
clock = pygame.time.Clock()
pulse_time = 0
last_time = time.time()
auto_rotate = False  # 默认关闭自动旋转，由手势控制

# 启动摄像头线程
camera_thread = threading.Thread(target=camera_thread_function)
camera_thread.daemon = True
camera_thread.start()

try:
    while running:
        loop_start_time = time.time()
        # 计算帧时间差
        current_time = time.time()
        dt = current_time - last_time
        last_time = current_time
        
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    running = False
                elif event.key == pygame.K_SPACE:
                    # 切换自动旋转
                    auto_rotate = not auto_rotate
                elif event.key == pygame.K_v:
                    # 切换预设视角
                    current_view = (current_view + 1) % len(favorite_views)
                    view = favorite_views[current_view]
                    rotation_x = view['x']
                    rotation_y = view['y']
                    rotation_scale = view['scale']
        
        # 设置处理超时
        max_gesture_process_time = 0.05  # 50毫秒
        gesture_process_start = time.time()
        
        # 处理手势控制
        try:
            with camera_image_lock:
                if time.time() - gesture_process_start > max_gesture_process_time:
                    print("手势处理超时，跳过本次更新")
                    continue
                    
                # 如果缩放大小已固定，使用固定值（即使手离开视野）
                if gesture_state.is_scale_fixed:
                    rotation_scale = gesture_state.fixed_scale
            
            # 握拳 - 2秒后解除固定状态，或重置视角
            if gesture_state.is_fist:
                # 只有当握拳超过2秒且大小已固定，才解除固定
                if gesture_state.is_scale_fixed and gesture_state.fist_duration >= 2.0:
                    # 这里不需要额外处理，因为在process_frame中已经设置了should_release_scale标志
                    pass
                # 否则如果大小未固定，则重置视角
                elif not gesture_state.is_scale_fixed:
                    view = favorite_views[0]
                    rotation_x = view['x']
                    rotation_y = view['y']
                    rotation_scale = view['scale']
                    auto_rotate = False
            
            # 手掌张开 - 控制旋转和缩放
            if gesture_state.is_hand_open:
                # 计算手掌移动差值，映射到旋转角度
                dx = (gesture_state.hand_x - gesture_state.last_hand_x) * 1000
                dy = (gesture_state.hand_y - gesture_state.last_hand_y) * 1000
                
                # 左右移动控制水平旋转
                rotation_y += dx
                
                # 上下移动控制垂直旋转，反转y轴因为屏幕坐标系y轴向下
                rotation_x -= dy  # 减号使得手向上移动时，视角向上
                
                # 限制上下旋转角度在合理范围内
                rotation_x = max(-45, min(45, rotation_x))
                
                # 只有在缩放比例未固定时，才根据手掌深度调整缩放
                if not gesture_state.is_scale_fixed:
                    # 反转逻辑：值越大(越近)缩小，值越小(越远)放大
                    target_scale = 2.5 - gesture_state.hand_z  # 反转深度计算目标缩放
                    target_scale = max(0.5, min(2.0, target_scale))  # 限制缩放范围
                    # 平滑过渡
                    rotation_scale = rotation_scale * 0.9 + target_scale * 0.1
                else:
                    # 如果缩放已固定，使用固定值
                    rotation_scale = gesture_state.fixed_scale
            
            # 食指指向 - 特殊操作，可以添加自定义功能
            if gesture_state.is_pointing:
                # 这里可以添加特殊操作
                pass
        
        except Exception as e:
            print(f"处理手势控制时出错: {e}")
        
        # 如果开启自动旋转
        if auto_rotate:
            rotation_y += 0.3 * dt * 60
        
        # 清除屏幕
        glClearColor(0.0, 0.0, 0.05, 1.0)
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        
        # 更新摄像头纹理
        acquired = safe_acquire_lock(camera_image_lock)
        if acquired:
            try:
                if camera_image is not None:
                    update_camera_texture(camera_image)
            finally:
                safe_release_lock(camera_image_lock)
        else:
            print("获取摄像头纹理锁超时")
        
        # 设置视角
        glLoadIdentity()
        glTranslatef(0.0, 0.0, -600 * rotation_scale)
        
        # 应用旋转
        glRotatef(rotation_x, 1, 0, 0)
        glRotatef(rotation_y, 0, 1, 0)
        
        # 更新小球
        for ball in balls:
            ball.update(dt)
        
        # 绘制容器
        glDisable(GL_LIGHTING)
        
        # 周期性变化容器颜色
        color_cycle_offset += color_cycle_speed * dt
        r = 0.4 + 0.1 * math.sin(color_cycle_offset)
        g = 0.4 + 0.1 * math.sin(color_cycle_offset + 2)
        b = 0.6 + 0.2 * math.sin(color_cycle_offset + 4)
        glColor4f(r, g, b, 0.3)
        
        # 使用wireframe方式绘制容器球体
        sphere = gluNewQuadric()
        gluQuadricDrawStyle(sphere, GLU_LINE)
        gluSphere(sphere, 200, 32, 32)
        gluDeleteQuadric(sphere)
        
        # 绘制连接线
        glLineWidth(1.5)
        
        glBegin(GL_LINES)
        line_count = 0
        for i, ball1 in enumerate(balls):
            ball_connections = 0
            max_ball_connections = 7
            
            for j, ball2 in enumerate(balls[i+1:], i+1):
                if line_count >= max_connections or ball_connections >= max_ball_connections:
                    break
                    
                distance = calculate_distance(ball1, ball2)
                if distance < connection_distance:
                    r = (ball1.color[0] + ball2.color[0]) / 2
                    g = (ball1.color[1] + ball2.color[1]) / 2
                    b = (ball1.color[2] + ball2.color[2]) / 2
                    
                    alpha = 0.8 * (1.0 - distance / connection_distance)
                    
                    glColor4f(r, g, b, alpha)
                    glVertex3f(ball1.x, ball1.y, ball1.z)
                    glVertex3f(ball2.x, ball2.y, ball2.z)
                    line_count += 1
                    ball_connections += 1
        glEnd()
        
        # 重新启用光照
        glEnable(GL_LIGHTING)
        
        # 脉动效果
        pulse_time += 0.8 * dt
        ambient = 0.2 + 0.1 * math.sin(pulse_time)
        light_ambient = [ambient, ambient, ambient, 1.0]
        glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient)
        
        # 移动光源
        light_x = 200 * math.sin(pulse_time * 0.3)
        light_y = 100 * math.sin(pulse_time * 0.5)
        light_z = 200 * math.cos(pulse_time * 0.3)
        light_position = [light_x, light_y, light_z, 1.0]
        glLightfv(GL_LIGHT0, GL_POSITION, light_position)
        
        # 绘制所有小球
        for ball in balls:
            ball.draw()
        
        # 显示摄像头视图
        draw_camera_view()
        
        # 更新显示
        pygame.display.flip()
        
        # 显示帧率和线条数量
        fps = clock.get_fps()
        pygame.display.set_caption(f"手势控制3D网络 - FPS: {fps:.1f} - 线条: {line_count} - 小球: {num_balls}")
        
        # 控制帧率
        clock.tick(60)

except Exception as e:
    print(f"渲染循环中发生错误: {e}")
    import traceback
    traceback.print_exc()

# 清理资源
glDeleteBuffers(3, [sphere_data['vertices'], sphere_data['normals'], sphere_data['indices']])

# 设置结束标志
running = False
time.sleep(0.5)  # 等待线程结束

# 退出
pygame.quit()
print("程序结束")
